package ds.test.fit;

import org.apache.commons.dbcp.BasicDataSource;
import org.dbunit.DatabaseUnitException;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.CachedDataSet;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.excel.XlsDataSet;
import org.dbunit.dataset.xml.FlatXmlProducer;
import org.dbunit.ext.oracle.OracleConnection;
import org.dbunit.operation.DatabaseOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.datasource.DelegatingDataSource;
import org.springframework.util.StringUtils;
import org.xml.sax.InputSource;

import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * User: yinwenbo Date: 12-8-10 Time: 上午10:19
 */
public class DbUtils {

	private static Logger logger = LoggerFactory.getLogger(DbUtils.class);

	public static IDatabaseConnection createConnection(String schema, DataSource dataSource) {
		try {
			writeLog(dataSource);
			return new OracleConnection(DataSourceUtils.getConnection(dataSource), schema);
		} catch (DatabaseUnitException e) {
			throw new RuntimeException(e);
		}
	}

	public static void cleanInsertData(String path, IDatabaseConnection connection) {
		try {
			IDataSet dataSet = createDateSet(path);
			DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
		} catch (DatabaseUnitException e) {
			throw new RuntimeException(e);
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public static String[] getTestDataSets(Class<?> type) {

		logger.debug("parse annotation @TestDataSet from class {} ", type.getName());
		List<String> list = new ArrayList<String>();
		putDataSetToList(type, list);

		return list.toArray(new String[0]);
	}

	public static String[] getTestDataSet(Method method) {

		logger.debug("parse annotation @TestDataSet from method {} ", method.getName());
		List<String> list = new ArrayList<String>();
		putDataSetToList(list, method.getDeclaredAnnotations());

		return list.toArray(new String[0]);
	}

	public static String[] getTestBackupTables(String path) {
		try {
			IDataSet dataSet = createDateSet(path);
			return dataSet.getTableNames();
		} catch (DatabaseUnitException e) {
			throw new RuntimeException(e);
		}
	}

	private static void putDataSetToList(Class<?> type, List<String> list) {

		Class<?> superType = type.getSuperclass();
		if (superType != null) {
			putDataSetToList(superType, list);
		}
		Annotation[] annotations = type.getDeclaredAnnotations();
		putDataSetToList(list, annotations);
	}

	private static void putDataSetToList(List<String> list, Annotation[] annotations) {

		for (Annotation annotation : annotations) {
			if (annotation instanceof TestDataSet) {
				TestDataSet testDataSet = (TestDataSet) annotation;
				for (String file : testDataSet.value()) {
					logger.debug("dataSet fileName: {}", file);
					list.add(file);
				}
			}
		}
	}

	private static CachedDataSet createDateSet(String path) {
		logger.debug("load file {} to dataSet.", path);
		try {
			CachedDataSet dataSet = null;
			if (StringUtils.endsWithIgnoreCase(path, ".xls")) {
				dataSet = createDataSetFromExcel(path);
			} else {
				dataSet = createDataSetFromXml(path);
			}
			DbUtils.writeLog(dataSet);
			return dataSet;
		} catch (DataSetException e) {
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	private static CachedDataSet createDataSetFromExcel(String path) throws DataSetException, IOException {
		return new CachedDataSet(new XlsDataSet(new File(path)));
	}

	private static CachedDataSet createDataSetFromXml(String path) throws DataSetException {
		FlatXmlProducer producer = new FlatXmlProducer(new InputSource(path));
		producer.setColumnSensing(true);
		return new CachedDataSet(producer);
	}

	private static void writeLog(IDataSet dataSet) throws DataSetException {
		for (String tableName : dataSet.getTableNames()) {
			int rows = dataSet.getTable(tableName).getRowCount();
			logger.debug("load {} rows to table {}.", rows, tableName);
		}
	}

	private static void writeLog(DataSource dataSource) {
		if (dataSource instanceof DelegatingDataSource) {
			DataSource ds = ((DelegatingDataSource) dataSource).getTargetDataSource();
			if (ds instanceof BasicDataSource) {
				BasicDataSource bds = (BasicDataSource) ds;
				logger.debug("dataSource url: {}, username: {}.", bds.getUrl(), bds.getUsername());
			}
		}
	}
}
